package com.gzj.environment.module.collectData.context;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.util.StrUtil;
import com.gzj.environment.constant.CollectDataConstant;
import com.gzj.environment.enums.code.impl.collectDataCode.CollectDataCode;
import com.gzj.environment.framework.exception.CustomerException;
import com.gzj.environment.model.collectData.criteria.QueryCriteria;
import io.netty.channel.ChannelHandlerContext;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 保存客户端信息
 */
@Slf4j
public class ClientContext {

    /**
     * 客户端注册表，格式：Map<type, Map<group, List<Map<channel信息map>>>>，
     * channel信息map的格式为{id: id, ip: ip, number: number, channel: ChannelHandlerContext}
     */
    private static final Map<String, Map<String, List<Map<String, Object>>>> clientMap = new ConcurrentHashMap<>();

    // 客户端版本信息Map<信道ip地址，版本号>
    private static final Map<String, String> versionMap = new ConcurrentHashMap<>();

    private static final BlockingQueue<EditTask> taskQueue = new LinkedBlockingQueue<>(200);

    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory());

    /**
     * 任务实体
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class EditTask {
        private String type;
        private String group;
        private String number;
        private List<Map<String, Object>> list;
        private String operation;
    }

    /**
     * 新增客户端
     * @param type 客户端类型
     * @param group 点位组
     * @param number 点位编号
     * @param ctx 信道信息
     */
    public static void addClient(String type, String group, String number, ChannelHandlerContext ctx) {
        // 1.如果没有type对应的map就创建
        clientMap.computeIfAbsent(type, k -> new HashMap<>());

        // 2.如果没有group对应的list就创建
        Map<String, List<Map<String, Object>>> groupMap = clientMap.get(type);
        groupMap.computeIfAbsent(group, k -> new ArrayList<>());

        List<Map<String, Object>> channelList = groupMap.get(group);
        List<Map<String, Object>> copiedChannelList = new ArrayList<>(channelList);

        // 3.创建信道资料并添加职对应的list
        Map<String, Object> channelMap = new HashMap<>();
        channelMap.put("id", ctx.channel().id().asShortText());
        channelMap.put("ip", ctx.channel().remoteAddress().toString());
        channelMap.put("number", number);
        channelMap.put("channel", ctx);
        copiedChannelList.add(channelMap);

        // 4.放入队列
        try {
            taskQueue.put(new EditTask(type, group, number, copiedChannelList,
                    CollectDataConstant.ADD_FOR_EDIT_CLIENT_MAP));
        } catch (InterruptedException e) {
            log.info("放入任务时发生异常", e);
        }

    }

    /**
     * 获取所有客户端信息
     * @return
     */
    public static Map<String, Map<String, List<Map<String, Object>>>> getClientMap() {
        return clientMap;
    }

    /**
     * 根据客户端类型获取客户端信息
     * @param type 客户端类型
     * @return
     */
    public static Map<String, List<Map<String, Object>>> getClientMapByType(String type) {
        return clientMap.get(type);
    }

    /**
     * 根据客户端类型和点位组名获取客户端信息
     * @param type 客户端类型
     * @param group 点位组
     * @return
     */
    public static List<Map<String, Object>> getClientMapByTypeAndGroup(String type, String group) {
        Map<String, List<Map<String, Object>>> typeMap = clientMap.get(type);
        if (CollectionUtil.isNotEmpty(typeMap)) {
            return typeMap.get(group);
        }
        return new ArrayList<>();
    }

    /**
     * 获取所有的通道信息
     * @return
     */
    public static List<ChannelHandlerContext> getAllChannels() {
        List<Map<String, List<Map<String, Object>>>> groupList = new ArrayList<>(clientMap.values());
        List<Map<String, Object>> list = new ArrayList<>();
        if (CollectionUtil.isEmpty(groupList)) {
            return new ArrayList<>();
        }
        // 获取所有的channel信息
        for (Map<String, List<Map<String, Object>>> groupMap : groupList) {
            addChannelInfoToList(list, groupMap);
        }
        return list.stream().map(item -> (ChannelHandlerContext) item.get("channel")).collect(Collectors.toList());
    }

    /**
     * 将groupMap（二级map）中信道的信息加入指定的集合中
     * @param list 指定集合
     * @param map groupMap，二级map
     */
    private static void addChannelInfoToList(List<Map<String, Object>> list, Map<String, List<Map<String, Object>>> map) {
        for (Map.Entry<String, List<Map<String, Object>>> entry : map.entrySet()) {
            List<Map<String, Object>> channelList = entry.getValue();
            if (CollectionUtil.isNotEmpty(channelList)) {
                list.addAll(channelList);
            }
        }
    }

    /**
     * 根据类型获取所有通道信息
     * @param type 客户端类型
     * @return
     */
    public static List<ChannelHandlerContext> getChannelsByType(String type) {
        Map<String, List<Map<String, Object>>> groupMap = clientMap.get(type);
        List<Map<String, Object>> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(groupMap)) {
            addChannelInfoToList(list, groupMap);
        }
        return list.stream().map(item -> (ChannelHandlerContext) item.get("channel")).collect(Collectors.toList());
    }

    /**
     * 根据类型和点位组名获取所有通道信息
     * @param type 客户端类型
     * @param group 组名
     * @return
     */
    public static List<ChannelHandlerContext> getChannelsByTypeAndGroup(String type, String group) {
        Map<String, List<Map<String, Object>>> groupMap = clientMap.get(type);
        List<Map<String, Object>> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(groupMap)) {
            for (Map.Entry<String, List<Map<String, Object>>> entry : groupMap.entrySet()) {
                String tmpGroup = entry.getKey();
                List<Map<String, Object>> channelList = entry.getValue();
                if (tmpGroup.equals(group)) {
                    list.addAll(channelList);
                    return list.stream().map(item -> (ChannelHandlerContext) item.get("channel")).collect(Collectors.toList());
                }
            }
        }
        return new ArrayList<>();
    }

    /**
     * 根据ip获取通道信息
     * @param ip 客户端ip
     * @return
     */
    public static List<ChannelHandlerContext> getChannelByIp(String ip) {
        List<ChannelHandlerContext> allChannels = getAllChannels();
        return allChannels.stream()
                .filter(item -> ip.equals(item.channel().remoteAddress().toString()))
                .collect(Collectors.toList());
    }

    /**
     * 根据id获取通道信息
     * @param id 客户端id
     * @return
     */
    public static List<ChannelHandlerContext> getChannelById(String id) {
        List<ChannelHandlerContext> allChannels = getAllChannels();
        return allChannels.stream()
                .filter(item -> id.equals(item.channel().id().asShortText()))
                .collect(Collectors.toList());
    }

    /**
     * 根据查询条件QueryCriteria查询信道信息
     * @param criteria 查询条件
     * @return
     */
    public static List<ChannelHandlerContext> getChannelByCriteria(QueryCriteria criteria) {
        List<ChannelHandlerContext> list;
        if (StrUtil.isNotBlank(criteria.getIp())) {
            list = getChannelByIp(criteria.getType());
        } else if (StrUtil.isNotBlank(criteria.getGroup())) {
            if (StrUtil.isNotBlank(criteria.getType())) {
                list = getChannelsByTypeAndGroup(criteria.getType(), criteria.getGroup());
            } else {
                throw new CustomerException(CollectDataCode.HAS_NO_TYPE_PARAM);
            }
        } else {
            if (StrUtil.isNotBlank(criteria.getType())) {
                list = getChannelsByType(criteria.getType());
            } else {
                list = getAllChannels();
            }
        }
        return list;
    }

    /**
     * 删除客户端
     * @param ctx 需要删除的通道信息
     */
    public static void removeClient(ChannelHandlerContext ctx) {
        // 1.获取type
        Map<String, String> infoMap = findChannelInfoForContext(ctx);
        String type = infoMap.get("type");
        String group = infoMap.get("group");
        String number = infoMap.get("number");
        // 2.获取该类型客户端的所有通道信息
        Map<String, List<Map<String, Object>>> groupMap = clientMap.get(type);
        List<Map<String, Object>> channelList = groupMap.get(group);
        // 3.删除该客户端
        List<Map<String, Object>> copiedChannelList = new ArrayList<>(channelList);
        List<Map<String, Object>> handledCopiedChannelList = copiedChannelList.stream()
            .filter(item -> !item.get("id").equals(ctx.channel().id().asShortText()))
            .collect(Collectors.toList());

        // 4.放入队列
        try {
            taskQueue.put(new EditTask(type, group, number, handledCopiedChannelList,
                    CollectDataConstant.REMOVE_FOR_EDIT_CLIENT_MAP));
        } catch (InterruptedException e) {
            log.info("放入任务时发生异常", e);
        }
    }

    /**
     * 根据通道信息找到通道对应的类型
     * @param ctx 通道信息
     * @return
     */
    public static Map<String, String> findChannelInfoForContext(ChannelHandlerContext ctx) {
        Map<String, String> infoMap = new HashMap<>();
        // 1.获取通道id
        String id = ctx.channel().id().asShortText();

        // 2.遍历去匹配id
        if (CollectionUtil.isEmpty(getClientMap())) {
            return infoMap;
        }
        for (Map.Entry<String, Map<String, List<Map<String, Object>>>> typeEntry : getClientMap().entrySet()) {
            String type = typeEntry.getKey();
            Map<String, List<Map<String, Object>>> groupMap = typeEntry.getValue();
            if (CollectionUtil.isEmpty(groupMap)) {
                return infoMap;
            }
            for (Map.Entry<String, List<Map<String, Object>>> groupEntry : groupMap.entrySet()) {
                String group = groupEntry.getKey();
                List<Map<String, Object>> channelList = groupEntry.getValue();
                if (CollectionUtil.isEmpty(channelList)) {
                    return infoMap;
                }
                for (Map<String, Object> map : channelList) {
                    if (map.get("id").equals(id)) {
                        infoMap.put("type", type);
                        infoMap.put("group", group);
                        infoMap.put("number", (String) map.get("number"));
                        return infoMap;
                    }
                }
            }
        }
        throw new CustomerException(CollectDataCode.HAS_NO_MESSAGE_TYPE_OR_GROUP);
    }

    /**
     * 如果该类型的客户端数量为0，那么删除该客户端类型
     * @param type 客户端类型
     * @param group 点位组
     */
    private static void removeTypeIfAbsent(String type, String group) {
        // 1.获取该类型客户端的所有通道信息
        Map<String, List<Map<String, Object>>> groupMap = clientMap.get(type);
        // 2.再根据组名获取对应的所有通道信息
        List<Map<String, Object>> channelList = groupMap.get(group);
        // 3.如果该类型该组的客户端数量为0，那么删除该类型该组对应的客户端列表
        if (channelList.size() == 0) {
            groupMap.remove(group);
        }
        // 4.上一步执行完后，如果该类型的客户端数量为0，那么删除该类型对应的客户端列表
        if (groupMap.values().size() == 0) {
            clientMap.remove(type);
        }
    }

    /**
     * 清空所有客户端
     */
    public static void removeAllClient() {
        clientMap.clear();
    }

    /**
     * 添加客户端的版本信息
     * @param ip 客户端ip
     * @param version 客户端版本
     */
    public static void addClientVersion(String ip, String version) {
        versionMap.put(ip, version);
    }

    /**
     * 根据信道列表，查询他们的客户端版本
     * @param list 信道列表
     * @return
     */
    public static Map<String, String> getClientVersion(List<ChannelHandlerContext> list) {
        Map<String, String> map = new HashMap<>();
        if (CollectionUtil.isEmpty(list)) {
            return map;
        }
        List<String> ips = list.stream().map(item -> item.channel().remoteAddress().toString()).collect(Collectors.toList());
        for (String ip : ips) {
            if (versionMap.containsKey(ip)) {
                map.put(ip, versionMap.get(ip));
            }
        }
        return map;
    }

    /**
     * 获取版本信息
     * @return
     */
    public static Map<String, String> getVersionMap() {
        return versionMap;
    }

    /**
     * 清空版本信息
     */
    public static void clearClientVersion() {
        versionMap.clear();
    }

    /**
     * 执行线程
     */
    public static void startEditClientMapTask() {
        log.info("EditClientMapTask开始执行");
        executor.execute(new EditClientMapTask());
    }

    private static class EditClientMapTask implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    log.info("正在读取clientMap修改任务");
                    EditTask task = taskQueue.take();
                    Map<String, List<Map<String, Object>>> groupMap = clientMap.get(task.getType());
                    groupMap.put(task.getGroup(), task.getList());
                    clientMap.put(task.getType(), groupMap);
                    if (task.getOperation().equals(CollectDataConstant.REMOVE_FOR_EDIT_CLIENT_MAP)) {
                        removeTypeIfAbsent(task.getType(), task.getGroup());
                    }
                } catch (Exception e) {
                    log.error("在修改clientMap时发生异常", e);
                }
            }
        }
    }
}
